package com.scnb.proxy.v08;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;

/**
 * 问题：想要记录坦克的移动时间
 * 最简单的方法：修改代码，记录时间
 * 问题2：如果无法改变源代码呢？
 * v04：使用继承引入耦合
 * v05：使用代理
 * v06：使用多种代理
 * 问题3：当有多种代理的时候，怎么实现代理的组合？使用继承？装饰器？
 * v07：代理的对象改成Movable类型，实现多种代理的组合new TankTimeProxy(new Tank())
 * v08：如果想让LogProxy可以重用，不仅可以代理Tank类，还可以代理任何其他可以代理的类型——分离代理行为与被代理对象，使用jdk动态代理
 */

interface Movable {
    void move();
}

public class Tank implements Movable {
    public void testMethod() {
        System.out.println("This is class Tank Method testMethod");
    }

    //模拟坦克移动了一段时间
    @Override
    public void move() {
        System.out.println("Tank moving ....");

        try {
            Thread.sleep(new Random().nextInt(5000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Tank tank = new Tank();
        //反射（reflection）：通过分析二进制字节码得到类的属性和方法

        //JDK动态代理
        //使用JDK动态代理，要求被代理类必须实现了接口，这样代理类才能在接口中知道自己要实现哪些方法
        //这样的话，被代理类中自己实现的（不存在于接口中的）方法，代理类是不能获取到的
        Movable m = (Movable) Proxy.newProxyInstance(Tank.class.getClassLoader(), //需要用哪个classLoader把代理类load到内存中
                new Class[]{Movable.class},     //代理对象应该实现哪些接口
                new LogHandler(tank)            //代理对象自己的操作
        );

        m.move();
    }
}

class LogHandler implements InvocationHandler {
    Tank tank;

    public LogHandler(Tank tank) {
        this.tank = tank;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("method " + method.getName() + " start ..");
        Object invoke = method.invoke(tank, args);
        System.out.println("method " + method.getName() + " end!");
        return invoke;
    }
}